package br.com.management.mentor.app.components.messageextractors;

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.PersistenceException;

import org.hibernate.exception.ConstraintViolationException;
import org.postgresql.util.PSQLException;
import org.postgresql.util.ServerErrorMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.NoSuchMessageException;

import br.com.management.mentor.app.components.ExceptionMessageExtractor;

public class PSQLExceptionMessageExtractor implements ExceptionMessageExtractor {

	@Autowired
	private Locale locale;
	
	@Autowired
	private ApplicationContext context;
	
	@Override
	public boolean canHandle( Exception e ) {
		if( ! ( e instanceof PersistenceException ) ) return false;
		PersistenceException pe = (PersistenceException) e;
		
		if( !( pe.getCause() instanceof ConstraintViolationException ) ) return false;
		
		ConstraintViolationException cve = (ConstraintViolationException) pe.getCause();
		
		return cve.getSQLException() instanceof PSQLException;
	}

	@Override
	public String getMessage( Exception e ) {
		PersistenceException pe = (PersistenceException) e;
		ConstraintViolationException cve = (ConstraintViolationException) pe.getCause();
		PSQLException psqle = (PSQLException) cve.getSQLException();		
		
		ServerErrorMessage sem = psqle.getServerErrorMessage();
		String errorCode = sem.getSQLState();
		
		DatabaseError dbError = DatabaseError.getByCode( errorCode );
		if( dbError != null ) return dbError.getMessage( context , sem , locale );
		
		return e.getMessage();
	}
	
	public static enum DatabaseError {
		
		UNIQUE_FIELD( "23505" ) {
			
			private String messageKey = "org.postgresql.util.PSQLException.Unique.message";
			
			private String messageByFieldKey = "org.postgresql.util.PSQLException.Unique.%s.message";
			
			private Pattern p = Pattern.compile( "Key \\((\\w+)\\)=\\(\\w+\\).*" );
			
			@Override
			public String getMessage( ApplicationContext context , ServerErrorMessage sem , Locale locale ) {
				
				Matcher matcher = p.matcher( sem.getDetail() );
				if( matcher.matches() ) {
					String field = matcher.group(1);
					
					try {
						return context.getMessage( String.format( messageByFieldKey , field ) , null , locale );
					} catch(NoSuchMessageException e) {}
					
				}
					
				return context.getMessage( messageKey , null , locale );
			}
		};
		
		private final String code;
		
		private DatabaseError( String code ) {
			this.code = code;
		}
		
		public static DatabaseError getByCode( String code ) {
			for ( DatabaseError pe : DatabaseError.values() ) {
				if( pe.code.equals( code ) ) return pe;
			}
			return null;
		}

		public abstract String getMessage( ApplicationContext context , ServerErrorMessage sem , Locale locale );
	}
}